#if PocketPC && !Smartphone
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using GeoFramework.IO;

namespace GeoFramework
{
    public enum PowerState
    {
        On,
        Suspended,
        Rebooting,
        Resumed,
        Off
    }

    public enum BatteryState
    {
        Unknown,
        NoBattery,
        Critical,
        Low,
        High
    }

#if Framework20

    internal sealed class PowerEventShutdownSignal
    {
		private object blah = new object();

        public PowerEventShutdownSignal() { }

        ~PowerEventShutdownSignal()
        {
            PowerEvent.Dispose();
        }
    }

    /// <summary>
    /// Summary description for PowerEvent.
    /// </summary>
    public 
#if Framework20
        static
#else
    sealed
#endif
        class PowerEvent //: IDisposable
    {
        //private SafeFileHandle QuitEvent;
        private static SafeFileHandle PowerMessageQueue;
        private static SafeFileHandle hQueue = null;
        private static Thread PowerThread;
        private static PowerState pPowerState = PowerState.On;
        private static BatteryState pBatteryState = BatteryState.Unknown;
        private static bool pIsACPowerEnabled;
        private static bool pIsClosed = true;
        private static bool pIsPowerThreadAlive;
        private static ManualResetEvent PowerEventWaitHandle = new ManualResetEvent(false);

        //public event EventHandler DevicePoweredOff;
        //public event EventHandler DevicePoweredOn;
        //public event EventHandler DeviceBootCompleted;
		public static event EventHandler BatteryLevelCritical;
        public static event EventHandler BatteryLevelLow;
        public static event EventHandler BatteryLevelHigh;
        public static event EventHandler BatteryLevelUnknown;
        public static event EventHandler BatteryNotPresent;
        //public event EventHandler DeviceIdle;
        public static event EventHandler DeviceResumed;
        public static event EventHandler DeviceSuspended;
        //public event EventHandler DeviceReset;
        public static event EventHandler BatteryPowerEnabled;
        public static event EventHandler ACPowerEnabled;
        private static PowerEventShutdownSignal ShutdownSignal;

        static PowerEvent()
        {
            if (Environment.OSVersion.Platform != PlatformID.WinCE)
                return;

            // Make an object which can trigger our finalizer
            ShutdownSignal = new PowerEventShutdownSignal();

            try
            {
                // Create message queue for power broadcasts
                PowerMessageQueue = new SafeFileHandle(NativeMethods.CreateMsgQueue("PowerEventWaitQueue", new MSGQUEUEOPTIONS()), true);
            }
            catch (MissingMethodException)
            {
				// Signal that we're shut down to prevent the finalizer or Dispose from doing anything
				pIsClosed = true;

                // If we got here, it means the target device does not have CE.NET. This API is not available in Windows CE 3.0
                throw new NotSupportedException("Power state events are only supported on Windows CE and Windows Mobile devices.");
            }

            /* The following code adds support for "Deploy to My Computer"
             * scenarios, which do not support power events.  So, just do
             * nothing, yet don't raise an error!
             */  

			pIsClosed = false;

            // Create listener thread
            PowerThread = new Thread(new ThreadStart(PowerThreadProc));

            if (Environment.OSVersion.Platform == PlatformID.WinCE)
                PowerThread.Priority = ThreadPriority.Normal;

#if Framework20
            PowerThread.IsBackground = true;
            PowerThread.Name = "GeoFramework Power Notification Thread (http://www.geoframeworks.com)";
#endif
            PowerThread.Start();
			
            // Register for all power notifications.
            IntPtr hNotif = NativeMethods.RequestPowerNotifications(
				PowerMessageQueue.DangerousGetHandle(),
                PowerEventType.PBT_POWERSTATUSCHANGE
                | PowerEventType.PBT_POWERINFOCHANGE
                | PowerEventType.PBT_RESUME
                | PowerEventType.PBT_TRANSITION);
            
        }

        public static BatteryState BatteryState
        {
            get
            {
                return pBatteryState;
            }
        }

        public static PowerState PowerState
        {
            get
            {
                return pPowerState;
            }
        }

        public static bool IsBatteryEnabled
        {
            get
            {
                return !pIsACPowerEnabled;
            }
        }

        public static bool IsACPowerEnabled
        {
            get
            {
                return pIsACPowerEnabled;
            }
        }

        #region Monitoring Thread

        // Listener thread
        private static void PowerThreadProc()
        {
            try
            {
				// Has the class already been disposed?
				if(pIsClosed)
					return;

                // Signal that we're started
                pIsPowerThreadAlive = true;
                PowerEventWaitHandle.Set();

                // Open a message queue to receive power notifications
                hQueue = new SafeFileHandle(
							NativeMethods.CreateMsgQueue("PowerEventWaitQueue", new MSGQUEUEOPTIONS()), true);

                // Start waiting for Message Queue to fill up, while checking for a termination request
                while (!pIsClosed) // && NativeMethods.WaitForSingleObject(QuitEvent, 0) != WAIT_OBJECT_0)
                {
                    try
                    {
                        // Did queue get some data?
                        if (NativeMethods.WaitForSingleObject(hQueue.DangerousGetHandle(), 100) != WAIT_OBJECT_0)
                            continue;

                        // Yes, let's read it
                        // 1024 byte buffer should be well enough. 
                        POWER_BROADCAST pb = new POWER_BROADCAST(1024);
                        int nRead, Flags;
                        NativeMethods.ReadMsgQueue(hQueue.DangerousGetHandle(), pb.Data, pb.Data.Length, out nRead, 0, out Flags);

                        // Is the power in transition?
                        switch (pb.Message)
                        {
                            case PowerEventType.PBT_TRANSITION:
                                {
                                    switch (pb.Flags)
                                    {
                                        //case PowerStates.POWER_STATE_BOOT:
                                        //    OnDeviceBootCompleted();
                                        //    break;
                                        case PowerStates.POWER_STATE_CRITICAL:
                                            OnBatteryLevelCritical();
                                            break;
                                        //case PowerStates.POWER_STATE_IDLE:
                                        //    OnDeviceIdle();
                                        //    break;
                                        //case PowerStates.POWER_STATE_OFF:
                                        //    OnDevicePoweredOff();
                                        //    break;
                                        //case PowerStates.POWER_STATE_ON:
                                        //    OnDevicePoweredOn();
                                        //    break;
                                        //case PowerStates.POWER_STATE_RESET:
                                        //    OnDeviceReset();
                                        //    break;
                                        case PowerStates.POWER_STATE_SUSPEND:
                                            OnDeviceSuspended();
                                            break;
                                        default:
                                            // convert the bytearray to a string
                                            //										StringBuilder buffer = new StringBuilder();
                                            //										for(int i=0; i < pb.Length;i++)
                                            //										{
                                            //											buffer.Append(pb.Data[i].ToString());
                                            //											buffer.Append(" ");
                                            //										}
                                            //										Log(DateTime.Now.ToLongTimeString() + ": Unrecognized transition: " + buffer.ToString());
                                            break;
                                    }
                                    break;
                                }
                            case PowerEventType.PBT_POWERSTATUSCHANGE:
                                {
                                    switch (pb.Flags)
                                    {
                                        default:
                                            // If POWERSTATE[21] is 
                                            // convert the bytearray to a string
                                            //										StringBuilder buffer = new StringBuilder();
                                            //										foreach(byte b in pb.SystemPowerState)
                                            //										{
                                            //											buffer.Append(b.ToString());
                                            //											buffer.Append(" ");
                                            //										}
                                            //										Log(DateTime.Now.ToLongTimeString() + ": Unrecognized power status: " + buffer.ToString());
                                            break;
                                    }
                                    break;
                                }
                            case PowerEventType.PBT_POWERINFOCHANGE:
                                {
                                    switch (pb.Flags)
                                    {
                                        default:
                                            {
                                                // Detect whether AC power is enabled
                                                switch (pb.SystemPowerState[20])
                                                {
                                                    case 0:
                                                        OnBatteryPowerEnabled();
                                                        break;
                                                    case 1:
                                                        OnACPowerEnabled();
                                                        break;
                                                }
                                                // Detect the battery status
                                                switch (pb.SystemPowerState[21])
                                                {
                                                    case 1:
                                                        OnBatteryLevelHigh();
                                                        break;
                                                    case 2:
                                                        OnBatteryLevelLow();
                                                        break;
                                                    case 4:
                                                        OnBatteryLevelCritical();
                                                        break;
                                                    case 8:
                                                        OnACPowerEnabled();
                                                        break;
                                                    case 128:
                                                        OnNoBattery();
                                                        break;
                                                    case 255:
                                                        OnBatteryLevelUnknown();
                                                        break;
                                                }
                                                break;
                                            }
                                    }
                                    break;
                                }
                            case PowerEventType.PBT_RESUME:
                                {
                                    OnDeviceResumed();
                                    break;
                                }
                            default:
                                {
                                    //								Log(DateTime.Now.ToLongTimeString() + ": Unrecognized message: " + pb.Message.ToString());
                                    break;
                                }
                        }
                    }
#if Framework20
                    catch (ThreadAbortException)
                    {
                        break;
                    }
#endif
                    catch
                    {
                        // Anything to do?
                    }
                    finally
                    {
                        // Let other threads work!
                        Thread.Sleep(0);
                    }
                }
            }
#if Framework20
            catch (ThreadAbortException)
            {
                // Close down the queue
                if (hQueue != null)
                    hQueue.Close();

                // Signal that we're done
                if (PowerEventWaitHandle.Handle != new IntPtr(-1))
                {
                    try
                    {
                        PowerEventWaitHandle.Set();
                    }
                    catch
                    {
                    }
                }

                // Signal that we're dead
                pIsPowerThreadAlive = false;
            }
#endif
            catch
            {
            }
            finally
            {
                // Close down the queue
                if(hQueue != null)
                    hQueue.Close();

                // Signal that we're done
                if (PowerEventWaitHandle.Handle != new IntPtr(-1))
                {
                    try
                    {
                        PowerEventWaitHandle.Set();
                    }
                    catch
                    {
                    }
                }

                // Signal that we're dead
                pIsPowerThreadAlive = false;

            }
        }

        #endregion

        #region Events

        //private void OnDeviceBootCompleted()
        //{
        //    // If there's no change, exit
        //    if(pPowerState == PowerState.On)
        //        return;
        //    // Set the new state
        //    pPowerState = PowerState.On;
        //    // And notify of the change
        //    try
        //    {
        //        if(DeviceBootCompleted != null)
        //            DeviceBootCompleted(null, EventArgs.Empty);
        //    }
        //    catch
        //    {
        //    }

        //    //			Log(DateTime.Now.ToLongTimeString() + ": Device boot completed.");

        //}

        private static void OnBatteryLevelCritical()
        {
            // If there's no change, exit
            if (pBatteryState == BatteryState.Critical)
                return;
            // Set the new state
            pBatteryState = BatteryState.Critical;
            // And notify of the change

            try
            {
                if (BatteryLevelCritical != null)
#if !Framework20
                    BatteryLevelCritical(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelCritical, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Battery level critical!");
        }

        private static void OnBatteryLevelHigh()
        {
            // If there's no change, exit
            if (pBatteryState == BatteryState.High)
                return;
            // Set the new state
            pBatteryState = BatteryState.High;
            // And notify of the change

            try
            {
                if (BatteryLevelHigh != null)
#if !Framework20
                    BatteryLevelHigh(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelHigh, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Battery level High!");
        }

        private static void OnBatteryLevelLow()
        {
            // If there's no change, exit
            if (pBatteryState == BatteryState.Low)
                return;
            // Set the new state
            pBatteryState = BatteryState.Low;
            // And notify of the change

            try
            {
                if (BatteryLevelLow != null)
#if !Framework20
                    BatteryLevelLow(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelLow, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Battery level Low!");
        }

        private static void OnNoBattery()
        {
            // If there's no change, exit
            if (pBatteryState == BatteryState.NoBattery)
                return;
            // Set the new state
            pBatteryState = BatteryState.NoBattery;
            // And notify of the change

            try
            {
                if (BatteryNotPresent != null)
#if !Framework20
                    BatteryNotPresent(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryNotPresent, null, EventArgs.Empty);
#endif           
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": No Battery!");
        }

        private static void OnBatteryLevelUnknown()
        {
            // If there's no change, exit
            if (pBatteryState == BatteryState.Unknown)
                return;
            // Set the new state
            pBatteryState = BatteryState.Unknown;
            // And notify of the change
            try
            {
                if (BatteryLevelUnknown != null)
#if !Framework20
                    BatteryLevelUnknown(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelUnknown, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Battery level unknown!");
        }

        private static void OnBatteryPowerEnabled()
        {
            // Are we already on battery power?
            if (!pIsACPowerEnabled)
                return;
            // Flag that battery power is enabled
            pIsACPowerEnabled = false;
            // Notify of the change
            try
            {
                if (BatteryPowerEnabled != null)
#if !Framework20
                    BatteryPowerEnabled(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryPowerEnabled, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Battery Power Enabled!");
        }

        private static void OnACPowerEnabled()
        {
            // Are we already on battery power?
            if (pIsACPowerEnabled)
                return;
            // Flag that battery power is enabled
            pIsACPowerEnabled = true;
            // Notify of the change
            try
            {
                if (ACPowerEnabled != null)
#if !Framework20
                    ACPowerEnabled(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(ACPowerEnabled, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": AC Power Enabled!");
        }

        //private void OnDeviceIdle()
        //{
        //    try
        //    {
        //        if(DeviceIdle != null)
        //            DeviceIdle(null, EventArgs.Empty);
        //    }
        //    catch
        //    {
        //    }

        //    //			Log(DateTime.Now.ToLongTimeString() + ": Device is idle");
        //}

        //private void OnDevicePoweredOff()
        //{
        //    // Are we already set to this state?
        //    if(pPowerState == PowerState.Off)
        //        return;
        //    // Set the new state
        //    pPowerState = PowerState.Off;
        //    // And raise the event
        //    try
        //    {
        //        if(DevicePoweredOff != null)
        //            DevicePoweredOff(null, EventArgs.Empty);
        //    }
        //    catch
        //    {
        //    }

        //    //			Log(DateTime.Now.ToLongTimeString() + ": Device was powered off");
        //}

        //private void OnDevicePoweredOn()
        //{
        //    // Are we already set to this state?
        //    if(pPowerState == PowerState.On)
        //        return;
        //    // Set the new state
        //    pPowerState = PowerState.On;
        //    // And raise the event
        //    try
        //    {
        //        if(DevicePoweredOn != null)
        //            DevicePoweredOn(null, EventArgs.Empty);
        //    }
        //    catch
        //    {
        //    }

        //    //			Log(DateTime.Now.ToLongTimeString() + ": Device was powered on");
        //}

        //private void OnDeviceReset()
        //{
        //    pPowerState = PowerState.Rebooting;

        //    try
        //    {
        //        if(DeviceReset != null)
        //            DeviceReset(null, EventArgs.Empty);
        //    }
        //    catch
        //    {
        //    }

        //    //			Log(DateTime.Now.ToLongTimeString() + ": Device was reset");
        //}

        private static void OnDeviceResumed()
        {
            // Are we already set to this state?
            if (pPowerState == PowerState.Resumed)
                return;
            // Set the new state
            pPowerState = PowerState.Resumed;
            // And raise the event
            try
            {
                if (DeviceResumed != null)
#if !Framework20
                    DeviceResumed(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(DeviceResumed, null, EventArgs.Empty);
#endif            
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Device was resumed");
        }

        private static void OnDeviceSuspended()
        {
            // Are we already set to this state?
            if (pPowerState == PowerState.Suspended)
                return;
            // Set the new state
            pPowerState = PowerState.Suspended;
            // And raise the event	
            try
            {
                if (DeviceSuspended != null)
#if !Framework20
                    DeviceSuspended(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(DeviceSuspended, null, EventArgs.Empty);
#endif
            }
            catch
            {
            }

            //			Log(DateTime.Now.ToLongTimeString() + ": Device was suspended");

        }
        #endregion

        #region P/Invoke Stuff

        //[DllImport("coredll")]
        //static extern IntPtr CreateMsgQueue(string Name, MSGQUEUEOPTIONS Options);

        //[DllImport("coredll")]
        //static extern bool ReadMsgQueue(
        //    IntPtr hMsgQ,
        //    byte[] lpBuffer,
        //    int cbBufferSize,
        //    out int lpNumberOfBytesRead,
        //    int dwTimeout,
        //    out int pdwFlags
        //    );

        //[DllImport("coredll")]
        //static extern IntPtr CreateEvent(int dwReserved1, bool bManualReset, bool bInitialState, string Name);

        //[DllImport("coredll")]
        //static extern bool EventModify(IntPtr hEvent, int func);

        //[DllImport("coredll")]
        //static extern bool CloseHandle(IntPtr h);

        //[DllImport("coredll")]
        //static extern IntPtr RequestPowerNotifications(IntPtr hMsgQ, PowerEventType Flags);

        //[DllImport("coredll")]
        //static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);

        const int WAIT_OBJECT_0 = 0;
        const int EVENT_SET = 3;
        #endregion

        #region IDisposable Members

        public static void Dispose()
		{            
            if (Environment.OSVersion.Platform != PlatformID.WinCE)
				return;

			if (pIsClosed)
				return;

            // Flag that we're closing/closed
            pIsClosed = true;

            // Unhook all events
            BatteryLevelCritical = null;
            BatteryLevelLow = null;
            BatteryLevelHigh = null;
            BatteryLevelUnknown = null;
            BatteryNotPresent = null;
            DeviceResumed = null;
            DeviceSuspended = null;
            BatteryPowerEnabled = null;
            ACPowerEnabled = null;

#if Framework20
            try
            {
                if (pIsPowerThreadAlive && PowerThread != null && !PowerThread.Join(2000))
                    PowerThread.Abort();            
            }
            catch
            {
            }
#else
            /* CF1 has no ability to forcefully abort a thread, so llet it exit on its own */
#endif

            if (PowerMessageQueue != null)
            {
                try
                {
                    // Clean up
                    PowerMessageQueue.Close();
                }
                catch
                {
                }
            }

            // Terminate the wait handle
            if (PowerEventWaitHandle.Handle != new IntPtr(-1))
            {
                try
                {
                    PowerEventWaitHandle.Close();
                }
                catch
                {
                }
            }
		}

        #endregion
    }


#else
	/// <summary>
	/// Summary description for PowerEvent.
	/// </summary>
	public sealed class PowerEvent : IDisposable
	{
		//private SafeFileHandle QuitEvent;
		private SafeFileHandle PowerMessageQueue;
		private SafeFileHandle hQueue = null;
		private Thread PowerThread;
		private PowerState pPowerState = PowerState.On;
		private BatteryState pBatteryState = BatteryState.Unknown;
		private bool pIsACPowerEnabled;
		private bool pIsClosed = true;
		private ManualResetEvent PowerEventWaitHandle = new ManualResetEvent(false);

		//public event EventHandler DevicePoweredOff;
		//public event EventHandler DevicePoweredOn;
		//public event EventHandler DeviceBootCompleted;
		public event EventHandler BatteryLevelCritical;
		public event EventHandler BatteryLevelLow;
		public event EventHandler BatteryLevelHigh;
		public event EventHandler BatteryLevelUnknown;
		public event EventHandler BatteryNotPresent;
		//public event EventHandler DeviceIdle;
		public event EventHandler DeviceResumed;
		public event EventHandler DeviceSuspended;
		//public event EventHandler DeviceReset;
		public event EventHandler BatteryPowerEnabled;
		public event EventHandler ACPowerEnabled;
#if Framework20
		private PowerEventShutdownSignal ShutdownSignal;
#endif
		private object PowerChangeSyncRoot = new object();
		private bool pIsPowerThreadAlive;

		public PowerEvent()
		{
			if (Environment.OSVersion.Platform != PlatformID.WinCE)
				return;

#if Framework20
			// Make an object which can trigger our finalizer
			ShutdownSignal = new PowerEventShutdownSignal();
#endif

			try
			{
				// Create message queue for power broadcasts
				PowerMessageQueue = new SafeFileHandle(NativeMethods.CreateMsgQueue("PowerEventWaitQueue", new MSGQUEUEOPTIONS()), true);
			}
			catch (MissingMethodException)
			{
				// Signal that we're shut down to prevent the finalizer or Dispose from doing anything
				pIsClosed = true;

				// If we got here, it means the target device does not have CE.NET. This API is not available in Windows CE 3.0
				throw new NotSupportedException("Power state events are only supported on Windows CE and Windows Mobile devices.");
			}

			/* The following code adds support for "Deploy to My Computer"
			 * scenarios, which do not support power events.  So, just do
			 * nothing, yet don't raise an error!
			 */  

			pIsClosed = false;

			// Create listener thread
			PowerThread = new Thread(new ThreadStart(PowerThreadProc));

			if (Environment.OSVersion.Platform == PlatformID.WinCE)
				PowerThread.Priority = ThreadPriority.Normal;

#if Framework20
            PowerThread.IsBackground = true;
            PowerThread.Name = "GeoFramework Power Notification Thread (http://www.geoframeworks.com)";
#endif
			PowerThread.Start();
			
			// Register for all power notifications.
			IntPtr hNotif = NativeMethods.RequestPowerNotifications(
				PowerMessageQueue.DangerousGetHandle(),
				PowerEventType.PBT_POWERSTATUSCHANGE
				| PowerEventType.PBT_POWERINFOCHANGE
				| PowerEventType.PBT_RESUME
				| PowerEventType.PBT_TRANSITION);
            
		}

		public BatteryState BatteryState
		{
			get
			{
				return pBatteryState;
			}
		}

		public PowerState PowerState
		{
			get
			{
				return pPowerState;
			}
		}

		public bool IsBatteryEnabled
		{
			get
			{
				return !pIsACPowerEnabled;
			}
		}

		public bool IsACPowerEnabled
		{
			get
			{
				return pIsACPowerEnabled;
			}
		}

		#region Monitoring Thread

		// Listener thread
		private void PowerThreadProc()
		{
			try
			{
				// Has the class already been disposed?
				if(pIsClosed)
					return;

				// Signal that we're started
				pIsPowerThreadAlive = true;
				PowerEventWaitHandle.Set();

				// Open a message queue to receive power notifications
				hQueue = new SafeFileHandle(
					NativeMethods.CreateMsgQueue("PowerEventWaitQueue", new MSGQUEUEOPTIONS()), true);

				// Start waiting for Message Queue to fill up, while checking for a termination request
				while (!pIsClosed) // && NativeMethods.WaitForSingleObject(QuitEvent, 0) != WAIT_OBJECT_0)
				{
					try
					{
						// Did queue get some data?
						if (NativeMethods.WaitForSingleObject(hQueue.DangerousGetHandle(), 100) != WAIT_OBJECT_0)
							continue;


						// Yes, let's read it
						// 1024 byte buffer should be well enough. 
						POWER_BROADCAST pb = new POWER_BROADCAST(1024);
						int nRead, Flags;
						NativeMethods.ReadMsgQueue(hQueue.DangerousGetHandle(), pb.Data, pb.Data.Length, out nRead, 0, out Flags);

						// Prevent other messages from being processed
						// until this one completes
						lock(PowerChangeSyncRoot)
						{
							// Is the power in transition?
							switch (pb.Message)
							{
								case PowerEventType.PBT_TRANSITION:
								{
									switch (pb.Flags)
									{
											//case PowerStates.POWER_STATE_BOOT:
											//    OnDeviceBootCompleted();
											//    break;
										case PowerStates.POWER_STATE_CRITICAL:
											OnBatteryLevelCritical();
											break;
											//case PowerStates.POWER_STATE_IDLE:
											//    OnDeviceIdle();
											//    break;
											//case PowerStates.POWER_STATE_OFF:
											//    OnDevicePoweredOff();
											//    break;
											//case PowerStates.POWER_STATE_ON:
											//    OnDevicePoweredOn();
											//    break;
											//case PowerStates.POWER_STATE_RESET:
											//    OnDeviceReset();
											//    break;
										case PowerStates.POWER_STATE_SUSPEND:
											OnDeviceSuspended();
											break;
										default:
											// convert the bytearray to a string
											//										StringBuilder buffer = new StringBuilder();
											//										for(int i=0; i < pb.Length;i++)
											//										{
											//											buffer.Append(pb.Data[i].ToString());
											//											buffer.Append(" ");
											//										}
											//										Log(DateTime.Now.ToLongTimeString() + ": Unrecognized transition: " + buffer.ToString());
											break;
									}
									break;
								}
								case PowerEventType.PBT_POWERSTATUSCHANGE:
								{
									switch (pb.Flags)
									{
										default:
											// If POWERSTATE[21] is 
											// convert the bytearray to a string
											//										StringBuilder buffer = new StringBuilder();
											//										foreach(byte b in pb.SystemPowerState)
											//										{
											//											buffer.Append(b.ToString());
											//											buffer.Append(" ");
											//										}
											//										Log(DateTime.Now.ToLongTimeString() + ": Unrecognized power status: " + buffer.ToString());
											break;
									}
									break;
								}
								case PowerEventType.PBT_POWERINFOCHANGE:
								{
									switch (pb.Flags)
									{
										default:
										{
											// Detect whether AC power is enabled
											switch (pb.SystemPowerState[20])
											{
												case 0:
													OnBatteryPowerEnabled();
													break;
												case 1:
													OnACPowerEnabled();
													break;
											}
											// Detect the battery status
											switch (pb.SystemPowerState[21])
											{
												case 1:
													OnBatteryLevelHigh();
													break;
												case 2:
													OnBatteryLevelLow();
													break;
												case 4:
													OnBatteryLevelCritical();
													break;
												case 8:
													OnACPowerEnabled();
													break;
												case 128:
													OnNoBattery();
													break;
												case 255:
													OnBatteryLevelUnknown();
													break;
											}
											break;
										}
									}
									break;
								}
								case PowerEventType.PBT_RESUME:
								{
									OnDeviceResumed();
									break;
								}
								default:
								{
									//								Log(DateTime.Now.ToLongTimeString() + ": Unrecognized message: " + pb.Message.ToString());
									break;
								}
							}
						}
					}
#if Framework20
                    catch (ThreadAbortException)
                    {
                        break;
                    }
#endif
					catch
					{
						// Anything to do?
					}
					finally
					{
						// Let other threads work!
						Thread.Sleep(100);
						Thread.Sleep(0);
					}
				}
			}
#if Framework20
            catch (ThreadAbortException)
            {
                // Close down the queue
                if (hQueue != null)
                    hQueue.Close();

                // Signal that we're done
                if (PowerEventWaitHandle.Handle != -1)
                {
                    try
                    {
                        PowerEventWaitHandle.Set();
                    }
                    catch
                    {
                    }
                }

                // Signal that we're dead
                pIsPowerThreadAlive = false;
            }
#endif
			catch
			{
			}
			finally
			{
				// Close down the queue
				if(hQueue != null)
					hQueue.Close();

				// Signal that we're done
				if (PowerEventWaitHandle.Handle != new IntPtr(-1))
				{
					try
					{
						PowerEventWaitHandle.Set();
					}
					catch
					{
					}
				}

				// Signal that we're dead
				pIsPowerThreadAlive = false;

			}
		}

		#endregion

		#region Events

		//private void OnDeviceBootCompleted()
		//{
		//    // If there's no change, exit
		//    if(pPowerState == PowerState.On)
		//        return;
		//    // Set the new state
		//    pPowerState = PowerState.On;
		//    // And notify of the change
		//    try
		//    {
		//        if(DeviceBootCompleted != null)
		//            DeviceBootCompleted(null, EventArgs.Empty);
		//    }
		//    catch
		//    {
		//    }

		//    //			Log(DateTime.Now.ToLongTimeString() + ": Device boot completed.");

		//}

		private void OnBatteryLevelCritical()
		{
			// If there's no change, exit
			if (pBatteryState == BatteryState.Critical)
				return;
			// Set the new state
			pBatteryState = BatteryState.Critical;
			// And notify of the change

			try
			{
				if (BatteryLevelCritical != null)
#if !Framework20
					BatteryLevelCritical(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelCritical, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Battery level critical!");
		}

		private void OnBatteryLevelHigh()
		{
			// If there's no change, exit
			if (pBatteryState == BatteryState.High)
				return;
			// Set the new state
			pBatteryState = BatteryState.High;
			// And notify of the change

			try
			{
				if (BatteryLevelHigh != null)
#if !Framework20
					BatteryLevelHigh(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelHigh, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Battery level High!");
		}

		private void OnBatteryLevelLow()
		{
			// If there's no change, exit
			if (pBatteryState == BatteryState.Low)
				return;
			// Set the new state
			pBatteryState = BatteryState.Low;
			// And notify of the change

			try
			{
				if (BatteryLevelLow != null)
#if !Framework20
					BatteryLevelLow(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelLow, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Battery level Low!");
		}

		private void OnNoBattery()
		{
			// If there's no change, exit
			if (pBatteryState == BatteryState.NoBattery)
				return;
			// Set the new state
			pBatteryState = BatteryState.NoBattery;
			// And notify of the change

			try
			{
				if (BatteryNotPresent != null)
#if !Framework20
					BatteryNotPresent(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryNotPresent, null, EventArgs.Empty);
#endif           
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": No Battery!");
		}

		private void OnBatteryLevelUnknown()
		{
			// If there's no change, exit
			if (pBatteryState == BatteryState.Unknown)
				return;
			// Set the new state
			pBatteryState = BatteryState.Unknown;
			// And notify of the change
			try
			{
				if (BatteryLevelUnknown != null)
#if !Framework20
					BatteryLevelUnknown(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryLevelUnknown, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Battery level unknown!");
		}

		private void OnBatteryPowerEnabled()
		{
			// Are we already on battery power?
			if (!pIsACPowerEnabled)
				return;
			// Flag that battery power is enabled
			pIsACPowerEnabled = false;
			// Notify of the change
			try
			{
				if (BatteryPowerEnabled != null)
#if !Framework20
					BatteryPowerEnabled(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(BatteryPowerEnabled, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Battery Power Enabled!");
		}

		private void OnACPowerEnabled()
		{
			// Are we already on battery power?
			if (pIsACPowerEnabled)
				return;
			// Flag that battery power is enabled
			pIsACPowerEnabled = true;
			// Notify of the change
			try
			{
				if (ACPowerEnabled != null)
#if !Framework20
					ACPowerEnabled(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(ACPowerEnabled, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": AC Power Enabled!");
		}

		//private void OnDeviceIdle()
		//{
		//    try
		//    {
		//        if(DeviceIdle != null)
		//            DeviceIdle(null, EventArgs.Empty);
		//    }
		//    catch
		//    {
		//    }

		//    //			Log(DateTime.Now.ToLongTimeString() + ": Device is idle");
		//}

		//private void OnDevicePoweredOff()
		//{
		//    // Are we already set to this state?
		//    if(pPowerState == PowerState.Off)
		//        return;
		//    // Set the new state
		//    pPowerState = PowerState.Off;
		//    // And raise the event
		//    try
		//    {
		//        if(DevicePoweredOff != null)
		//            DevicePoweredOff(null, EventArgs.Empty);
		//    }
		//    catch
		//    {
		//    }

		//    //			Log(DateTime.Now.ToLongTimeString() + ": Device was powered off");
		//}

		//private void OnDevicePoweredOn()
		//{
		//    // Are we already set to this state?
		//    if(pPowerState == PowerState.On)
		//        return;
		//    // Set the new state
		//    pPowerState = PowerState.On;
		//    // And raise the event
		//    try
		//    {
		//        if(DevicePoweredOn != null)
		//            DevicePoweredOn(null, EventArgs.Empty);
		//    }
		//    catch
		//    {
		//    }

		//    //			Log(DateTime.Now.ToLongTimeString() + ": Device was powered on");
		//}

		//private void OnDeviceReset()
		//{
		//    pPowerState = PowerState.Rebooting;

		//    try
		//    {
		//        if(DeviceReset != null)
		//            DeviceReset(null, EventArgs.Empty);
		//    }
		//    catch
		//    {
		//    }

		//    //			Log(DateTime.Now.ToLongTimeString() + ": Device was reset");
		//}

		private void OnDeviceResumed()
		{
			// Are we already set to this state?
			if (pPowerState == PowerState.Resumed)
				return;
			// Set the new state
			pPowerState = PowerState.Resumed;
			// And raise the event
			try
			{
				if (DeviceResumed != null)
#if !Framework20
					DeviceResumed(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(DeviceResumed, null, EventArgs.Empty);
#endif            
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Device was resumed");
		}

		private void OnDeviceSuspended()
		{
			// Are we already set to this state?
			if (pPowerState == PowerState.Suspended)
				return;
			// Set the new state
			pPowerState = PowerState.Suspended;
			// And raise the event	
			try
			{
				if (DeviceSuspended != null)
#if !Framework20
					DeviceSuspended(null, EventArgs.Empty);
#else
                    DynamicEvent.Invoke(DeviceSuspended, null, EventArgs.Empty);
#endif
			}
			catch
			{
			}

			//			Log(DateTime.Now.ToLongTimeString() + ": Device was suspended");

		}
		#endregion

		#region P/Invoke Stuff

		//[DllImport("coredll")]
		//public extern IntPtr CreateMsgQueue(string Name, MSGQUEUEOPTIONS Options);

		//[DllImport("coredll")]
		//public extern bool ReadMsgQueue(
		//    IntPtr hMsgQ,
		//    byte[] lpBuffer,
		//    int cbBufferSize,
		//    out int lpNumberOfBytesRead,
		//    int dwTimeout,
		//    out int pdwFlags
		//    );

		//[DllImport("coredll")]
		//public extern IntPtr CreateEvent(int dwReserved1, bool bManualReset, bool bInitialState, string Name);

		//[DllImport("coredll")]
		//public extern bool EventModify(IntPtr hEvent, int func);

		//[DllImport("coredll")]
		//public extern bool CloseHandle(IntPtr h);

		//[DllImport("coredll")]
		//public extern IntPtr RequestPowerNotifications(IntPtr hMsgQ, PowerEventType Flags);

		//[DllImport("coredll")]
		//public extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);

		const int WAIT_OBJECT_0 = 0;
		const int EVENT_SET = 3;
		#endregion

		#region IDisposable Members

		public void Dispose()
		{            
			if (Environment.OSVersion.Platform != PlatformID.WinCE)
				return;

			if (pIsClosed)
				return;

			// Flag that we're closing/closed
			pIsClosed = true;

			// Unhook all events
			BatteryLevelCritical = null;
			BatteryLevelLow = null;
			BatteryLevelHigh = null;
			BatteryLevelUnknown = null;
			BatteryNotPresent = null;
			DeviceResumed = null;
			DeviceSuspended = null;
			BatteryPowerEnabled = null;
			ACPowerEnabled = null;

//			// Wait up to two seconds for a graceful exit
//			for (int i = 0; i < 20; i++)
//			{
//				Thread.Sleep(100);
//				Thread.Sleep(0);
//
//				// Was it exited?
//				if (!pIsPowerThreadAlive)
//					// Yes. 
//					break;
//
//				// No.  Keep waiting
//			}

#if Framework20
            if (pIsPowerThreadAlive)
            {
                // Abort the thread
                try
                {
					if(PowerThread != null && !PowerThread.Join(2000))
						PowerThread.Abort();

				    if (hQueue != null)
                        hQueue.Close();
				}
                catch
                {
                }
            }
#else
			try
			{
				// Wait for the thread to exit
				PowerEventWaitHandle.WaitOne();
			}
			catch
			{
			}
#endif



			try
			{
				// Clean up
				PowerMessageQueue.Close();
			}
			catch
			{
			}

			// Terminate the wait handle
			if (PowerEventWaitHandle.Handle != new IntPtr(-1))
			{
				try
				{
					PowerEventWaitHandle.Close();
				}
				catch
				{
				}
			}
		}

		void IDisposable.Dispose()
		{
			Dispose();
		}

		#endregion
	}


#endif
	
	#region Internal Structures

	[Flags]
    internal enum PowerEventType
    {
        PBT_TRANSITION = 0x00000001,
        PBT_RESUME = 0x00000002,
        PBT_POWERSTATUSCHANGE = 0x00000004,
        PBT_POWERINFOCHANGE = 0x00000008,
    }

    [Flags]
    internal enum PowerStates
    {
        POWER_STATE_ON = (0x00010000),
        POWER_STATE_OFF = (0x00020000),

        POWER_STATE_CRITICAL = (0x00040000),
        POWER_STATE_BOOT = (0x00080000),
        POWER_STATE_IDLE = (0x00100000),
        POWER_STATE_SUSPEND = (0x00200000),
        POWER_STATE_RESET = (0x00800000),
    }

    internal sealed class MSGQUEUEOPTIONS
    {
        public MSGQUEUEOPTIONS()
        {
            dwSize = Marshal.SizeOf(typeof(MSGQUEUEOPTIONS));
            dwFlags = 0;
            dwMaxMessages = 20;
            cbMaxMessage = 100;
            bReadAccess = 1;
        }
        public int dwSize;
        public int dwFlags;
        public int dwMaxMessages;
        public int cbMaxMessage;
        public int bReadAccess;
    }

    internal sealed class POWER_BROADCAST_POWER_INFO
    {
        byte[] m_data;

        public POWER_BROADCAST_POWER_INFO(int size)
        {
            m_data = new byte[size];
        }

        public byte[] Data { get { return m_data; } }
    }

    internal sealed class POWER_BROADCAST
    {
        public POWER_BROADCAST(int size)
        {
            m_data = new byte[size];
        }
        byte[] m_data;
        public byte[] Data { get { return m_data; } }
        public PowerEventType Message { get { return (PowerEventType)BitConverter.ToInt32(m_data, 0); } }
        public PowerStates Flags { get { return (PowerStates)BitConverter.ToInt32(m_data, 4); } }
        public int Length { get { return BitConverter.ToInt32(m_data, 8); } }
        public byte[] SystemPowerState { get { byte[] data = new byte[Length]; Buffer.BlockCopy(m_data, 12, data, 0, Length); return data; } }
    }

	#endregion

}
#endif